/* Copyright 2009 Sony Mathew, Minneapolis MN. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
package com.xsm.gwt.widgets.client;

import java.util.Iterator;
import java.util.NoSuchElementException;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * A way to nest a bunch of SimplePanels (i.e. <div>) to apply css classes and styles on them.
 * This is like a decorator panel - you can finally add a panel to this decorator panel to wrap it.
 * 
 * Example Usage:
 * 
 * TextBox myText = new TextBox();
 * CSSPanel cssPanel = new CSSPanel().css("class1").css("class2").inner().css("class3").inner().css("class3")
 * cssPanel.add(myContent);
 *
 * or with AxisPanel.decor(p)
 * 
 * AxisPanel ap = new AxisPanel()
 *  .x(myText).id("id-1").width("100%").decor(new CSSPanel().css("class1").css("class2"))
 *  
 * @author Sony Mathew
 */
public class CSSPanel extends Composite implements HasWidgets {

    private SimplePanel root = new SimplePanel();
    private SimplePanel target = root;
    private SimplePanel inmost = root;
    private Widget content = null;
    
    /**
     * author Sony Mathew
     */
    public CSSPanel() {        
        initWidget(root);
    }
    
    /**
     * Navigate to the inner SimplePanel (i.e. <div>) of the current target.  Create if necessary. 
     *  All operations will now target this inner SimplePanel.
     *  
     * author Sony Mathew
     */
    public CSSPanel inner() {        
        Widget innerWidget = target.getWidget();
        if (innerWidget != null && innerWidget == content) {
            throw new IllegalStateException("No nore inner CSS panel, reached content of CSSPanel");
        }
        
        SimplePanel inner = (SimplePanel)innerWidget;
        if (inner == null) {
            inner = new SimplePanel();
            target.setWidget(inner);
            inmost = inner;
        }
        
        target = inner;        
        return this;
    }

    /**
     * Navigate to the outer SimplePanel (i.e. <div>) of the current target.
     *  All operations will now target this outer SimplePanel.
     *  
     * author Sony Mathew
     */
    public CSSPanel outer() {        
        if (target == root) {
            throw new IllegalStateException("No more outer CSS panel, reached root of CSSPanel");
        }
        
        SimplePanel outer = (SimplePanel)target.getParent();
        if (outer == null) {
            throw new IllegalStateException("No more outer CSS panel exists");
        }
        
        target = outer;
        return this;
    }
        
    /**
     * Shortcut method chaining for wrapContent(Widget).
     * 
     * author Sony Mathew
     */
    public CSSPanel wrap(Widget w) {
        wrapContent(w);
        return this;
    }
    
    /**
     * Shortcut method chaining for wrapContent(new Label).
     * 
     * author Sony Mathew
     */
    public CSSPanel wrap(String label) {
        return wrap(new Label(label));
    }

    /**
     * Same as wrap()
     * Shortcut method chaining for wrapContent(Widget).
     * 
     * author Sony Mathew
     */
    public CSSPanel put(Widget w) {
        wrapContent(w);
        return this;
    }
    
    /**
     * Same as wrap()
     * Shortcut method chaining for wrapContent(new Label).
     * 
     * author Sony Mathew
     */
    public CSSPanel put(String label) {
        return wrap(new Label(label));
    }
    
    
//
//  HasWidgets impl.  
//    

    /**
     * @see com.google.gwt.user.client.ui.HasWidgets#add()
     */    
    public void add(Widget w) {
        wrapContent(w);
    }

    /**
     * @see com.google.gwt.user.client.ui.HasWidgets#clear()
     */
    public void clear() {
        removeContent();
    }

    /**
     * @see com.google.gwt.user.client.ui.HasWidgets#iterator()
     */
    public Iterator<Widget> iterator() {
        return new Iterator<Widget>() {
            boolean canNext = true;
            
            public boolean hasNext() {
                return (canNext && hasContent()); 
            }
            public Widget next() {
                if (hasNext()) {
                    canNext = false;
                    return getContent();
                }else{
                    throw new NoSuchElementException("No more content in CSSPanel");
                }
            }
            public void remove() {
                canNext = false;
                removeContent();
            }            
        };
    }

    /**
     * @see com.google.gwt.user.client.ui.HasWidgets#remove(com.google.gwt.user.client.ui.Widget)
     */
    public boolean remove(Widget w) {
        if (w == content) {
            removeContent();
            return true;
        }else{
            return false;
        }
    }

    /**
     * Set the content of the CSSPanel as the given widget.
     *  Same as the add(Widget) or content(Widget) methods.
     * 
     * Important:
     * Since by definition the CSSPanel (and its nesting of SimplePanels) decorates the content.
     *  The content is always added to the innermost SimplePanel (i.e. innermost <div>).
     *  
     * author Sony Mathew
     */
    public void wrapContent(Widget w) {
        content = w;
        inmost.setWidget(content);
    }
    
    /**
     * Same as wrapContent(Widget)
     * 
     * author Sony Mathew
     */
    public void setContent(Widget w) {
        wrapContent(w);
    }
    
    /**
     * Obtain the content of this CSSPanel.
     *  Held by the innermost SimplePanel.
     *  
     * author Sony Mathew
     */
    public Widget getContent() {
        return content;
    }
    
    /**
     * Check if there is already content.
     * 
     * author Sony Mathew
     */
    public boolean hasContent() {
        return (getContent() != null);
    }
    
    /**
     * Removes the 1 and only 1 content that the CSSPanel can hold.
     * 
     * author Sony Mathew
     */
    public void removeContent() {
        content = null;
        inmost.setWidget(null);
    }
        
//
//Operations available on the CSSPanel, that target the current target
//       use inner() or outer() to change targets.
//    
    
    /**
     * Apply the given cssClassName to current target SimplePanel.
     *  use inner() or outer() to change targets.
     * 
     * author Sony Mathew
     */
    public CSSPanel css(String cssClassName) {
        target.addStyleName(cssClassName);
        return this;
    }
    
    
    /**
     * Set an individual style attribute on the current target SimplePanel.
     *  use inner() or outer() to change targets.
     * 
     * author Sony Mathew
     */
    public CSSPanel style(String styleAttrib, String styleValue) {
        DOM.setStyleAttribute(target.getElement(), styleAttrib, styleValue);
        return this;
    }

    /**
     * Convenience for creating a solid border using styles with the given width and color.
     */
    public CSSPanel styleBorder(int width, String color) {
        style("borderStyle", "solid");
        style("borderColor", color);
        style("borderWidth", ""+width);
        return this;
    }

    /**
     * Convenience for creating a solid border using styles with default color and the given width. 
     */
    public CSSPanel styleBorder(int width) {
        style("borderStyle", "solid");
        style("borderColor", "#BADBEB");
        style("borderWidth", "1");
        return this;
    }
    
    /**
     * @see com.google.gwt.user.client.ui.UIObject#setVisible(boolean)
     */
    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);
        root.setVisible(visible);
    }
       
    /**
     * Convenience for method chaining.
     * 
     * author Sony Mathew
     */
    public CSSPanel visible(boolean visible) {
        setVisible(visible);
        return this;
    }
    
    /**
     * Set the Css width.
     */
    public CSSPanel width(String cssWidth) {
        super.setWidth(cssWidth);
        root.setWidth(cssWidth);
        return this;
    }
    
    /**
     * Set the Css height.
     */
    public CSSPanel height(String cssHeight) {
        super.setHeight(cssHeight);
        root.setHeight(cssHeight);
        return this;
    }
    
    /**
     * Shortcut for width("100%") + height("100%")
     * 
     * author Sony Mathew
     */
    public CSSPanel stretch() {
        width("100%");
        height("100%");
        return this;
    }
    
}
